package ch.epfl.lara.pm.transformation;

import ch.epfl.lara.pm.ast._

/**
 * Transform external constructs into simpler ones.
 * It basically map the external syntax to the internal one.
 */
object Desugarizer {

  
  def apply(tree: Tree): Tree = tree match {
    case Program(constr,objs,clss,main) => 
      Program(constr,objs.map(this(_).asInstanceOf[ObjectDef]),clss.map(this(_).asInstanceOf[ClassDef]),this(main).asInstanceOf[Expr])  
  
    case ObjectDef(name,extr) => 
      ObjectDef(name,this(extr).asInstanceOf[ExtractorDef])
    
    case ExtractorDef(arg,tpe,body) => 
      ExtractorDef(arg,tpe,this(body).asInstanceOf[Expr])
    
    case ClassDef(isAbs,name,fields,superc,methods) => 
      ClassDef(isAbs,name,fields,superc,methods.map(this(_).asInstanceOf[MethodDef]))
    
    case pd @ ParamDef(_,_) => pd
  
    case MethodDef(name,params,tpe,body) => 
      MethodDef(name,params,tpe,this(body).asInstanceOf[Expr])
    
    case Match(scrutinee,tpe,cases) =>
      Match(this(scrutinee).asInstanceOf[Expr],tpe,cases.map(this(_).asInstanceOf[CaseDef]))
      
    case CaseDef(position,pattern,guard,body) => 
      CaseDef(position,pattern,guard,this(body).asInstanceOf[Expr])
    
    case ClassPattern(_,_) | VarPattern(_,_) => tree
    
    case Var(_) | True | False | INone | Number(_) => tree
    
    case Call(obj,method,args) => Call(this(obj).asInstanceOf[Expr],method,args.map(this(_).asInstanceOf[Expr]))
    
    case Select(obj,field) => Select(this(obj).asInstanceOf[Expr],field)
    
    case If(cond,then,elze) => 
      import ch.epfl.lara.pm.analyze.Util.freshVarName
      import ch.epfl.lara.pm.global.ClassTable.root
      val thenKase = CaseDef(0,VarPattern(PatternName(freshVarName("x")),ClassType(root)),Some(cond),this(then).asInstanceOf[Expr])
      val elseKase = CaseDef(1,VarPattern(PatternName(freshVarName("x")),ClassType(root)),None,elze.asInstanceOf[Expr])
      Match(New(root,Nil),ClassType(root),List(thenKase,elseKase))
      
    case New(name,params) => New(name,params.map(this(_).asInstanceOf[Expr]))
      
    case IsInstanceOf(expr,tpeName) => IsInstanceOf(this(expr).asInstanceOf[Expr],tpeName)
    
    case Plus(left,right) => Plus(this(left).asInstanceOf[Expr],this(right).asInstanceOf[Expr])
    
    case Minus(left,right) => Minus(this(left).asInstanceOf[Expr],this(right).asInstanceOf[Expr])
    
    case Prod(left,right) => Prod(this(left).asInstanceOf[Expr],this(right).asInstanceOf[Expr])
    
    case Div(left,right) => Div(this(left).asInstanceOf[Expr],this(right).asInstanceOf[Expr])
    
    case Mod(left,right) => Mod(this(left).asInstanceOf[Expr],this(right).asInstanceOf[Expr])
    
    case Eq(left,right) => Eq(this(left).asInstanceOf[Expr],this(right).asInstanceOf[Expr])
    
    case Less(left,right) => Less(this(left).asInstanceOf[Expr],this(right).asInstanceOf[Expr])
    
    case LessEq(left,right) => LessEq(this(left).asInstanceOf[Expr],this(right).asInstanceOf[Expr])
    
    case EAnd(left,right) => EAnd(this(left).asInstanceOf[Expr],this(right).asInstanceOf[Expr])
    
    case EOr(left,right) => EOr(this(left).asInstanceOf[Expr],this(right).asInstanceOf[Expr])
    
    case Not(expr) => Not(this(expr).asInstanceOf[Expr])
    
    case NotEq(left,right) => NotEq(this(left).asInstanceOf[Expr],this(right).asInstanceOf[Expr])
    
    case _ => tree
  }
  
  
}
